package com.GPS_based_training.progetto_so2_2014_2015;

import java.text.DecimalFormat;
import java.util.Arrays;

import org.json.JSONException;
import org.json.JSONObject;

import com.GPS_based_training.JSONUtils.JSONFileManager;
import com.GPS_based_training.JSONUtils.JSONStandardization;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeries;
import com.GPS_based_training.progetto_so2_2014_2015.R;
import com.GPS_based_training.support.Training;
import com.GPS_based_training.support.TrainingElement;

import android.support.v7.app.ActionBarActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author Danilo Dessi' & Andreas Scalas
 * This activity shows the details about training
 */

public class TrainingDetailsActivity extends ActionBarActivity implements OnTouchListener{

	/******* CONTANTS TO MANAGE VIEW *******/
	private static final int FIRST_PAGE = 0;
	private static final int LAST_PAGE = 4;
	private static final float MIN_DISTANCE = 20;
	
	/******* CONSTANT TO MANAGE GRAPHS *******/
	private final String PACE = "pace";
	private final String SPEED = "speed";
	private final String DISTANCE ="distance";
	private final String TIME = "time";
	
	/******* CONSTANTS TO MANAGE CONVERSIONS OF THE VALUE *******/
	private final double MULTIPLIER_FROM_METERS_TO_KILOMETERS = 0.001;
	private final double DIVISOR_FROM_SECONDS_TO_MINUTES = 60;
	private final double MULTIPLIER_FROM_MILLISECONDS_TO_SECONDS = 1000;
	private final int RATIO_MILLISECONDS_SECONDS = 1000;
	private final int RATIO_SECONDS = 60;
	private final int RATIO_MINUTES = 60;
	private final int RATIO_HOURS = 3600;
	
	/******* OTHER CONSTANTS *******/
	//Constants to retrieve in the name and date of the json object 
	private final String NAME = "name";
	private final String DATE = "date";
		
	//A path to save file in the device. It is necessary to send file.
	private final String PATH_FILE = "file:///sdcard//Android/data/com.GPS-Based-Training.GPS-Based-Training/file";
	
	/******* GRAPHICS OBJECT *******/
	//The XYPlot objects represent the graph to describe training
	private XYPlot xyTimeSpeed;
	private XYPlot xyTimePace;
	private XYPlot xyDistanceSpeed;
	private XYPlot xyDistancePace;
	
	//The TextView view objects are used to show total information about training
	private TextView tvDate;
	private TextView tvDuration;
	private TextView tvMeanSpead;
	private TextView tvMeanPace;
	private TextView tvTotalDistance;
	private TextView tvName;

	/******* VARIABLES TO MANAGE TRAINING *******/
	private Training training; 			//This object that describe the training
	private String trainingJSONString; 	//This object that contains a training in json format
	private Bundle bundleIn; 			//bundleIn is a object that is used to receive information by caller 
	
	/******* VARIABLES TO MANAGE VIEW *******/
	private short page;
	private float y1, y2;
	
	
	
	/******* LIFE-CYCLE METHODS *******/
	
	@Override
	/**
	 * @author Danilo Dessi' & Andreas Scalas
	 * This method creates the activity
	 */
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_training_details);
		
		//Retrieval information from caller; the information is a string in json format that describes the training
	    bundleIn = getIntent().getExtras();
	    trainingJSONString = bundleIn.getString("JSONObject");
	    LinearLayout ll = (LinearLayout) findViewById(R.id.principal_details_layout);
	    ll.setOnTouchListener(this);
	    ImageView arrow_up = (ImageView) findViewById(R.id.arrow_up);
	    ImageView arrow_down = (ImageView) findViewById(R.id.arrow_down);
	    
	    arrow_up.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				turnBack();
				
			}
		});
	    
	    arrow_down.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				turnForward();
				
			}
		}); 
	    
	    
	    page = 0;
	    y1 = 0;
	    y2 = 0;
	    
	    //The link between objects graphic interface elements
		tvDate = (TextView) findViewById(R.id.tv_date_value_training_details);
		tvDuration = (TextView) findViewById(R.id.tv_duration_value_training_details);
		tvMeanSpead = (TextView) findViewById(R.id.tv_mean_speed_value_training_details);
		tvMeanPace = (TextView) findViewById(R.id.tv_mean_pace_value_training_details);
		tvTotalDistance = (TextView) findViewById(R.id.tv_total_distance_value_training_details);
		tvName = (TextView) findViewById(R.id.tv_name_training_details);
		xyTimeSpeed = (XYPlot) findViewById(R.id.xy_time_speed);
		xyTimePace = (XYPlot) findViewById(R.id.xy_time_pace);
		xyDistanceSpeed = (XYPlot) findViewById(R.id.xy_distance_speed);
		xyDistancePace = (XYPlot) findViewById(R.id.xy_distance_pace);
		
		try {
			
			//creates the new standardizer
			JSONStandardization standardJSON = new JSONStandardization();

			//convert the trainingString in Training
			training = standardJSON.jsonObjectToTraining(trainingJSONString);

		} catch (JSONException e) {
			
			Toast.makeText(this, getString(R.string.json_format_error), Toast.LENGTH_LONG ).show();
			
			e.printStackTrace();
			
		}
		
		//We set up the title of activity; the title is the name of the training
		this.setTitle(training.getName());
		
		//We set up the values of the training in the TextView objects 
		tvName.setText(training.getName());
		tvDate.setText(training.getDate());
		tvDuration.setText( formatTime( training.totalTime()) );
		tvMeanSpead.setText( formatDecimalValue( training.meanSpeed() ) + " " + getString(R.string.measurement_unit_speed));
		tvMeanPace.setText( formatDecimalValue( training.meanPace() ) + " " + getString(R.string.measurement_unit_pace));
		tvTotalDistance.setText( formatDecimalValue(training.totalDistance() * MULTIPLIER_FROM_METERS_TO_KILOMETERS) + " " + getString(R.string.measurement_unit_distance));
		
		//We prepare a line that can be shown in the graphs; this is a line to speed
		LineAndPointFormatter seriesSpeedFormat = new LineAndPointFormatter();
        seriesSpeedFormat.setPointLabelFormatter(null);
        seriesSpeedFormat.configure(getApplicationContext(),R.xml.formatter_speed_line);
        
        //We prepare a line that can be shown in the graphs; this is a line to pace
        LineAndPointFormatter seriesPaceFormat = new LineAndPointFormatter();
        seriesPaceFormat.setPointLabelFormatter(null);
        seriesPaceFormat.configure(getApplicationContext(),R.xml.formatter_pace_line);
        
        //We set up the values for the graphs. 
        xyTimeSpeed.addSeries(createXYSeries(training, TIME, SPEED), seriesSpeedFormat);
        xyTimeSpeed.setTicksPerRangeLabel(3);
        xyTimeSpeed.getGraphWidget().setDomainLabelOrientation(-45);
        
        xyTimePace.addSeries(createXYSeries(training, TIME, PACE), seriesPaceFormat);
        xyTimePace.setTicksPerRangeLabel(3);
        xyTimePace.getGraphWidget().setDomainLabelOrientation(-45);
        
        xyDistanceSpeed.addSeries(createXYSeries(training, DISTANCE, SPEED), seriesSpeedFormat);
        xyDistanceSpeed.setTicksPerRangeLabel(3);
        xyDistanceSpeed.getGraphWidget().setDomainLabelOrientation(-45);
        
        xyDistancePace.addSeries(createXYSeries(training, DISTANCE, PACE), seriesPaceFormat);
        xyDistancePace.setTicksPerRangeLabel(3);
        xyDistancePace.getGraphWidget().setDomainLabelOrientation(-45);
		
	}
	
	@Override
	/**
	 * @author Danilo Dessi'
	 * This method deletes the activity. It delete all files on devices that was created
	 */
	public void onDestroy(){
		super.onDestroy();
		
		JSONFileManager.deleteAllFile();
	}
	
	
	
	/******* CONTENT MENU *******/
	
	@Override
	/**
	 * @author Danilo Dessi'
	 * 
	 * This method creates the options menu
	 */
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.training_details, menu);
		return true;
	}

	@Override
	/**
	 * @author Danilo Dessi'
	 * 
	 * This method associates the menu items with the actions 
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		
		int id = item.getItemId();
		if (id == R.id.send) {
			sendFile();
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
	
	
	
	/******* METHODS TO SUPPORT THE CREATION OF THE GRAPHS *******/

	/**
	 * @author Danilo Dessi'
	 * 
	 * This method create series that are shown in the graphs
	 * @param training The training that must be described
	 * @param axisX A string to indicate the values of the axis X
	 * @param axisY A string to indicate the values of the axis Y
	 * @return A XYSeries that can be shown in the graphs
	 */
	private XYSeries createXYSeries(Training training, String axisX, String axisY) {
		int index = 0; //index of arrays
		String labelInGraphs;// value of the label in the graphs
		
		//Retrieval size of the array that contains trainingElement
		int size = training.getDetections().size();
		
		//We create 2 arrays to save values.
		Number [] xNumber = new Number[size];
		Number [] yNumber = new Number[size];
		
		for(TrainingElement e : training.getDetections()) {
			
			//we retrieve the distance/time values
			if(axisX.equals(DISTANCE)) {
				
				xNumber[index] = e.getDistance() * MULTIPLIER_FROM_METERS_TO_KILOMETERS;
			
			} else {
				
				xNumber[index] = e.getTime() / DIVISOR_FROM_SECONDS_TO_MINUTES;
			
			}
			
			//we retrieve the speed/pace values
			if(axisY.equals(SPEED)) {
				
				yNumber[index] = e.getSpeed();
			
			} else {
				
				yNumber[index] = e.getPace();
			
			}
			
			index++;
			
		}
		
		//we set up the value of the label in the graph
		if(axisY.equals(SPEED)) {
			
			labelInGraphs = getString(R.string.speed);
			
		} else {
			
			labelInGraphs = getString(R.string.pace);
		}
		
		//return a SimpleXYSeries to be shown in the graph
		return new SimpleXYSeries( Arrays.asList(xNumber), Arrays.asList(yNumber), labelInGraphs);
		
	}
	
	
	
	/******* METHODS TO SUPPORT THE FUNCTIONS OF THE ACTIVITY *******/
	
	/**
	 * @author Danilo Dessi'
	 * 
	 * This method is used to send current training
	 */
	private void sendFile(){
		
		//Intent to call the app to send file
		Intent intentSend = new Intent(Intent.ACTION_SEND);
		
		//We indicate the type of information that are sent
    	intentSend.setType("plain/text"); 
    	
    	try {
    		
			//we create the json object from the json string
			JSONObject jo = new JSONObject(trainingJSONString);
			
			//we insert the object of the email
			intentSend.putExtra(Intent.EXTRA_SUBJECT, "Allenamento : " + jo.getString(NAME) + " : " + jo.getString(DATE));
			
			//we insert a text of email
        	intentSend.putExtra(android.content.Intent.EXTRA_TEXT, getString(R.string.message_email));
        	
        	//we create a file on device
			JSONFileManager.saveOnFile(jo, jo.getString(NAME));
			
			//we insert the file as attachment
			Uri file = Uri.parse(PATH_FILE + "/" + jo.getString(NAME) + ".json");
        	intentSend.putExtra(Intent.EXTRA_STREAM, file);
        	
        	//If there is a app that manages the send action the new activity is launched
        	if (intentSend.resolveActivity(getPackageManager()) != null) {
        		startActivity(Intent.createChooser(intentSend, getString(R.string.title_send_file)));
        	} else {
        		Toast.makeText(this, getString(R.string.no_app_to_send_file) , Toast.LENGTH_SHORT).show();
        	}
        	
    	
    	} catch (Exception e) {
			e.printStackTrace();
		}   	
    	
	}
	
	/**
	 * @author Danilo Dessi'
	 * 
	 * This method is used to format double value
	 * @param value A java double
	 * @return A decimal format with 2 decimal digits
	 */
	private static String formatDecimalValue (double value){
		
		DecimalFormat decimalFormat = new DecimalFormat("0.00");
		
		return decimalFormat.format( value );
		
	}
	
	/**
	 * @author Danilo Dessi'
	 * 
	 * This method is used to format and convert a time
	 * @param time The value of time in second
	 * @return the time that is formatted in a string
	 */
	private String formatTime(double time) {
		
		//trasform the time in a long value
		int timeManage = (int) (time * MULTIPLIER_FROM_MILLISECONDS_TO_SECONDS);
		
		//we compute the milliseconds
		int milliSeconds = timeManage % RATIO_MILLISECONDS_SECONDS;
		timeManage = (timeManage - milliSeconds) / RATIO_MILLISECONDS_SECONDS;
		
		//we compute the seconds
		int seconds = timeManage % RATIO_SECONDS;
		
		//we compute the minutes
		int minutes = timeManage / RATIO_MINUTES;
		
		//we compute the hours
		int hours = timeManage / RATIO_HOURS;
		
		return hours + "h " + minutes + "m " + seconds + "s " + milliSeconds + "ms";
		
	}
	
	
	
	/******* METHODS TO MANAGE TOUCH ON THE VIEW *******/
	@Override
	/**
	 * @author Andreas Scalas
	 */
	public boolean onTouch(View v, MotionEvent event) {
		
		float deltaY;
		
		switch(event.getAction()){
		
	      case MotionEvent.ACTION_DOWN:
	          y1 = event.getY();                         
	          break;   
	          
	      case MotionEvent.ACTION_UP:
	          y2 = event.getY();
	          
	          deltaY = y2 - y1;

	          if (Math.abs(deltaY) > MIN_DISTANCE){
	        	  
	              if (y2 > y1){
	            	  
	            	//Up to Down swipe action
	                if(page > FIRST_PAGE) 
	                	turnBack();
	                
	              }else{
	            	  
	            	//Down to Up swipe action
	            	if(page < LAST_PAGE)
	            		turnForward();  
	            	
	              }

	          }else{
	        	  
	        	  //The touch event is a click.
	        	  v.performClick();
	        	  
	          }
	          
	      break;   
	    }  
		return true;
	}
	
	/**
	 * @author Andreas Scalas
	 * This method turns the page forward, according to actual page
	 */
	private void turnForward() {
		
		LinearLayout page0, page1, page2, page3, page4;
		ImageView arrow_up, arrow_down;
		
		//Graphic components
		page0 = (LinearLayout) findViewById(R.id.page0);
		page1 = (LinearLayout) findViewById(R.id.page1);
		page2 = (LinearLayout) findViewById(R.id.page2);
		page3 = (LinearLayout) findViewById(R.id.page3);
		page4 = (LinearLayout) findViewById(R.id.page4);
		arrow_up = (ImageView) findViewById(R.id.arrow_up);
		arrow_down = (ImageView) findViewById(R.id.arrow_down);
		
		//The page is turned according to actual page
		switch(this.page){
		
			//The turn of the pages consist on making the next page visible and on removing the actual page
		
			case 0://If the user is in the first page and he wants to go forward, the upward arrow has to be set visible
				
				page0.setVisibility(View.GONE);
				page0.invalidate();
				page1.setVisibility(View.VISIBLE);
				page1.invalidate();
				arrow_up.setVisibility(View.VISIBLE);
				break;
			
			case 1:
				
				page1.setVisibility(View.GONE);
				page1.invalidate();
				page2.setVisibility(View.VISIBLE);
				page2.invalidate();
				break;
				
			case 2:
				
				page2.setVisibility(View.GONE);
				page2.invalidate();
				page3.setVisibility(View.VISIBLE);
				page3.invalidate();
				break;
			
			case 3: //If the page is the last page, the user can't go further, so the downward arrow is made invisible.
				
				arrow_down.setVisibility(View.INVISIBLE);
				page3.setVisibility(View.GONE);
				page3.invalidate();
				page4.setVisibility(View.VISIBLE);
				page4.invalidate();
				break;
				
			default: 
				break;
		
		}
		
		page++; //The index of the page is incremented
		
	}

	/**
	 * @author Andreas Scalas
	 * This method turns the page back, according to actual page
	 */
	private void turnBack() {
		
		LinearLayout page0, page1, page2, page3, page4;
		ImageView arrow_up, arrow_down;
		
		//Graphic components
		page0 = (LinearLayout) findViewById(R.id.page0);
		page1 = (LinearLayout) findViewById(R.id.page1);
		page2 = (LinearLayout) findViewById(R.id.page2);
		page3 = (LinearLayout) findViewById(R.id.page3);
		page4 = (LinearLayout) findViewById(R.id.page4);
		arrow_up = (ImageView) findViewById(R.id.arrow_up);
		arrow_down = (ImageView) findViewById(R.id.arrow_down);
		
		//The page is turned according to actual page
		switch(this.page){
		
			//The turn of the pages consist on making the previous page visible and on removing the actual page
		
			case 1://If the page is the first page, the user can't go further, so the upward arrow is made invisible.
				
				arrow_up.setVisibility(View.INVISIBLE);
				page0.setVisibility(View.VISIBLE);
				page0.invalidate();
				page1.setVisibility(View.GONE);
				page1.invalidate();
				break;
			
			case 2:
				
				page1.setVisibility(View.VISIBLE);
				page1.invalidate();
				page2.setVisibility(View.GONE);
				page2.invalidate();
				break;
				
			case 3:
				
				page2.setVisibility(View.VISIBLE);
				page2.invalidate();
				page3.setVisibility(View.GONE);
				page3.invalidate();
				break;
			
			case 4://If the user is in the last page and he wants to go back, the downward arrow has to be set visible
				
				arrow_down.setVisibility(View.VISIBLE);
				page3.setVisibility(View.VISIBLE);
				page3.invalidate();
				page4.setVisibility(View.GONE);
				page4.invalidate();
				break;
				
			default: 
				break;
		
		}
		
		this.page--; //The index of the page is decreased.
		
	}
}
